/**
 * Copyright 2015 florent37, Inc.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.florent37.materialtextfield;

import ohos.accessibility.ability.AccessibleAbility;
import ohos.accessibility.ability.SoftKeyBoardController;
import ohos.agp.animation.Animator;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.StackLayout;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;

import java.io.IOException;

/**
 * Material text field
 */
public class MaterialTextField extends StackLayout {
    private static final Color DEFAULT_LABEL_COLOR = Color.BLACK;
    private static final int DEFAULT_ANIMATION_DURATION = 400;
    private static final String TAG = "MaterialTextField";
    private Color mBackgroundColor;
    private boolean mOpenKeyboardOnFocus = true;
    private int mAnimationDuration;
    private boolean mHasFocus = false;
    private Element mImage;
    private Color mLabelColor;
    private float mCardCollapsedHeight;
    private Text label;
    private ComponentContainer card;
    private Image image;
    private TextField editText;
    private StackLayout editTextLayout;
    private int labelTopMargin = -1;
    private boolean expanded = false;
    private float reducedScale = 0.2f;
    private Context context;

    public MaterialTextField(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public MaterialTextField(Context context, AttrSet attrs) {
        super(context, attrs);
        this.context = context;
        new StyledAttributes(attrs);
        init();
    }

    public MaterialTextField(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        new StyledAttributes(attrs);
        init();
    }

    private void init() {
        new EventHandler(EventRunner.getMainEventRunner())
                .postTask(
                        new Runnable() {
                            @Override
                            public void run() {
                                postLayout();
                            }
                        },
                        500);
    }

    /**
     * Toggle the view
     */
    public void toggle() {
        if (expanded) {
            reduce();
        } else {
            expand();
        }
    }

    /**
     * Collapse view
     */
    public void reduce() {
        if (expanded) {
            label.createAnimatorProperty()
                    .alpha(1)
                    .scaleX(1)
                    .scaleY(1)
                    .moveToY(card.getMarginTop() + (card.getHeight() / 2))
                    .setDuration(mAnimationDuration)
                    .start();

            image.createAnimatorProperty().scaleX(0.4f).scaleY(0.4f).setDuration(mAnimationDuration).start();

            editText.createAnimatorProperty()
                    .alpha(1f)
                    .setStateChangedListener(
                            new Animator.StateChangedListener() {
                                @Override
                                public void onStart(Animator animator) {
                                    if (expanded) {
                                        editText.setVisibility(Component.VISIBLE);
                                    }
                                }

                                @Override
                                public void onStop(Animator animator) {
                                    if (!expanded) {
                                        editText.setVisibility(Component.INVISIBLE);
                                    }
                                }

                                @Override
                                public void onCancel(Animator animator) {}

                                @Override
                                public void onEnd(Animator animator) {
                                    if (!expanded) {
                                        editText.setVisibility(Component.INVISIBLE);
                                    }
                                }

                                @Override
                                public void onPause(Animator animator) {}

                                @Override
                                public void onResume(Animator animator) {}
                            })
                    .start();
            card.createAnimatorProperty().scaleY(reducedScale).setDuration(mAnimationDuration).start();

            if (editText.hasFocus()) {
                KeyboardUtils.hideSoftKeyboard();
                editText.clearFocus();
            }
            expanded = false;
        }
    }

    /**
     * Expand view
     */
    public void expand() {
        if (!expanded) {
            if (editText.getVisibility() == INVISIBLE) {
                editText.setVisibility(VISIBLE);
            }
            editText.createAnimatorProperty().alpha(1f).scaleY(1).scaleX(1).setDuration(mAnimationDuration).start();
            card.createAnimatorProperty().scaleY(1f).setDuration(mAnimationDuration).start();
            label.createAnimatorProperty()
                    .alpha(0.6f)
                    .scaleX(0.8f)
                    .scaleY(0.8f)
                    .moveToY(-labelTopMargin)
                    .setDuration(mAnimationDuration)
                    .start();
            image.createAnimatorProperty().alpha(1f).scaleX(1f).scaleY(1f).setDuration(mAnimationDuration).start();

            if (editText != null) {
                editText.requestFocus();
            }
            if (mOpenKeyboardOnFocus) {
                KeyboardUtils.showSoftKeyboard();
            }
            expanded = true;

            editText.simulateClick();
        }
    }

    /**
     * Background color for the card
     *
     * @param color background color
     */
    public void setBackgroundColor(Color color) {
        this.mBackgroundColor = color;
    }

    /**
     * Background color
     *
     * @return color
     */
    public Color getBackgroundColor() {
        LogUtil.info(TAG, "getBackgroundColor() Value is : " + mBackgroundColor);
        return this.mBackgroundColor;
    }

    /**
     * get card
     *
     * @return card
     */
    public Component getCard() {
        LogUtil.info(TAG, "getCard() Value is : " + card);
        return card;
    }

    /**
     * get Label view
     *
     * @return label
     */
    public Text getLabel() {
        LogUtil.info(TAG, "getLabel() Value is : " + label);
        return label;
    }

    /**
     * get left drawer icon
     *
     * @return image
     */
    public Image getImage() {
        LogUtil.info(TAG, "getImage() Value is : " + image);
        return image;
    }

    /**
     * Get Edit text view
     *
     * @return editText
     */
    public TextField getEditText() {
        LogUtil.info(TAG, "getEditText() Value is : " + editText);
        return editText;
    }

    /**
     * get EditText layout
     *
     * @return editTextLayout
     */
    public ComponentContainer getEditTextLayout() {
        LogUtil.info(TAG, "getEditTextLayout() Value is : " + editTextLayout);
        return editTextLayout;
    }

    /**
     * Is card view expand
     *
     * @return expanded
     */
    public boolean isExpanded() {
        LogUtil.info(TAG, "isExpanded() Value is : " + expanded);
        return expanded;
    }

    /**
     * Set the focus to TextField
     *
     */
    public void setHasFocus() {
        if (mHasFocus) {
            expand();
        } else {
            reduce();
        }
    }

    private TextField findEditTextChild() {
        if (getChildCount() > 0 && getComponentAt(0) instanceof TextField) {
            return (TextField) getComponentAt(0);
        }
        return null;
    }

    @Override
    public void postLayout() {
        editText = findEditTextChild();
        if (editText == null) {
            return;
        }

        this.removeAllComponents();
        this.addComponent(LayoutScatter.getInstance(context).parse(ResourceTable.Layout_mtf_layout, this, false));
        editTextLayout = (StackLayout) this.findComponentById(ResourceTable.Id_mtf_editTextLayout);
        editTextLayout.addComponent(editText);
        card = (ComponentContainer) this.findComponentById(ResourceTable.Id_mtf_card);

        label = (Text) this.findComponentById(ResourceTable.Id_mtf_label);
        label.setPivotX(0);
        label.setPivotY(0);

        if (editText.getHint() != null) {
            label.setText(editText.getHint());
            editText.setHint("");
        }

        if (mBackgroundColor != null) {
            ShapeElement shapeElement1 = new ShapeElement();
            shapeElement1.setRgbColor(RgbColor.fromArgbInt(mBackgroundColor.getValue()));
            card.setBackground(shapeElement1);
        }

        int expandedHeight = (int) getFloatValueFormId(ResourceTable.Float_mtf_cardHeight_final);
        final int reducedHeight = (int) mCardCollapsedHeight;

        reducedScale = (float) (reducedHeight * 1.0 / expandedHeight);
        card.setScaleY(reducedScale);
        card.setPivotY(expandedHeight);

        image = (Image) this.findComponentById(ResourceTable.Id_mtf_image);
        image.setAlpha(0);
        image.setScaleX(0.4f);
        image.setScaleY(0.4f);

        editText.setAlpha(0f);
        labelTopMargin = label.getMarginTop();
        customizeFromAttributes();

        this.setClickedListener(
                new ClickedListener() {
                    @Override
                    public void onClick(Component component) {
                        toggle();
                    }
                });

        setHasFocus();
    }

    /**
     * Customize attributes
     */
    protected void customizeFromAttributes() {
        if (mLabelColor != null) {
            this.label.setTextColor(mLabelColor);
        }

        if (image != null) {
            this.image.setImageElement(mImage);
        }
    }

    /**
     * StyledAttributes
     */
    private class StyledAttributes {
        private String mtf_cardCollapsedHeight = "mtf_cardCollapsedHeight";

        private String mtf_labelColor = "mtf_labelColor";

        private String mtf_image = "mtf_image";

        private String mtf_hasFocus = "mtf_hasFocus";

        private String mtf_animationDuration = "mtf_animationDuration";

        private String mtf_openKeyboardOnFocus = "mtf_openKeyboardOnFocus";

        private String mtf_backgroundColor = "mtf_backgroundColor";

        StyledAttributes(AttrSet attrs) {
            mCardCollapsedHeight =
                    attrs.getAttr(mtf_cardCollapsedHeight).isPresent()
                            ? attrs.getAttr(mtf_cardCollapsedHeight).get().getDimensionValue()
                            : getFloatValueFormId(ResourceTable.Float_mtf_cardHeight_initial);

            mLabelColor =
                    attrs.getAttr(mtf_labelColor).isPresent()
                            ? attrs.getAttr(mtf_labelColor).get().getColorValue()
                            : DEFAULT_LABEL_COLOR;

            if (attrs.getAttr(mtf_image).isPresent()) {
                mImage = attrs.getAttr(mtf_image).get().getElement();
            } else {
                Utils.getElementByResId(context, ResourceTable.Media_icon).get();
            }

            mHasFocus =
                    attrs.getAttr(mtf_hasFocus).isPresent() ? attrs.getAttr(mtf_hasFocus).get().getBoolValue() : false;

            mAnimationDuration =
                    attrs.getAttr(mtf_animationDuration).isPresent()
                            ? attrs.getAttr(mtf_animationDuration).get().getIntegerValue()
                            : DEFAULT_ANIMATION_DURATION;

            mOpenKeyboardOnFocus =
                    attrs.getAttr(mtf_openKeyboardOnFocus).isPresent()
                            ? attrs.getAttr(mtf_openKeyboardOnFocus).get().getBoolValue()
                            : true;

            mBackgroundColor =
                    attrs.getAttr(mtf_backgroundColor).isPresent()
                            ? attrs.getAttr(mtf_backgroundColor).get().getColorValue()
                            : null;
        }
    }

    private float getFloatValueFormId(int resourceId) {
        try {
            return context.getResourceManager().getElement(resourceId).getFloat();
        } catch (IOException | NotExistException | WrongTypeException e) {
            LogUtil.error("Exception", e.toString());
        }
        return 0;
    }
}
